package com.gupao.mvcframework.v2.servlet;

import com.gupao.demo.mvc.action.DemoAction;
import com.gupao.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;

public class GPDispatcherServlet extends HttpServlet {

    private Properties configContext = new Properties();//配置容器
    private List<String> classNames = new ArrayList<String>();//存放类的容器
    private Map<String, Object> ioc = new HashMap<String, Object>();//IOC容器，存放bean的容器
    private Map<String, Method> handlerMapping = new HashMap<String, Method>();
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2、扫描相关类
        doScanner(configContext.getProperty("scanPackage"));
        //3、初始化所有相关的类的实例，并且放在IOC容器中
        doInstance();
        //4、完成依赖注入
        doAutoWired();
        //5、初始化HandlerMapping，及url和方法的关系
        initHandlerMapping();
    }

    //加载配置文件
    private void doLoadConfig(String contextConfigLocation) {
        InputStream is = null;
        try {
            is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            this.configContext.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(is != null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //扫描相关类
    private void doScanner(String scanPackage) {
        URL url = null;
        url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPaths = new File(url.getFile());
        for(File classPath : classPaths.listFiles() ){
            if(classPath.isDirectory()){
                doScanner(scanPackage + "." + classPath.getName());
            }else{
                if(classPath.getName().endsWith(".clsss")){
                    continue;
                }
                classNames.add((scanPackage + "." + classPath.getName()).replaceFirst(".class", ""));
            }
        }

    }

    //初始化所有相关的类的实例，并且放在IOC容器中
    private void doInstance() {
        if(classNames.isEmpty()){
            return;
        }
        try {
            for(String className : classNames){
                Class<?> clazz = Class.forName(className);
                if(clazz.isAnnotationPresent(GPController.class)){
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(GPService.class)){
                    String beanName = "";
                    GPService gpService = clazz.getAnnotation(GPService.class);
                    if("".equals(gpService.value())){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }else{
                        beanName = gpService.value();
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                }else{
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //完成依赖注入
    private void doAutoWired() {
        if(ioc.isEmpty()){
            return;
        }
        for(Map.Entry<String, Object> entry : ioc.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for(Field field : fields){
                field.isAnnotationPresent(GPAutowired.class);
                GPAutowired gpAutowired = field.getAnnotation(GPAutowired.class);
                String beanName = "";
                if("".equals(gpAutowired.value().trim())){
                    beanName = field.getType().getName();
                }else{
                    beanName = gpAutowired.value().trim();
                }
                field.setAccessible(true);//设置私有属性访问全文
                try {
                    //执行注入动作
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    //初始化HandlerMapping
    private void initHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }
        for(Map.Entry<String, Object> entry : ioc.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            String baseUrl = "";
            if(!clazz.isAnnotationPresent(GPRequestMapping.class)){
                continue;
            }
            GPRequestMapping gpRequestMapping = clazz.getAnnotation(GPRequestMapping.class);
            baseUrl = gpRequestMapping.value();
            Method[] methods = clazz.getMethods();
            for(Method method : methods){
                if(!method.isAnnotationPresent(GPRequestMapping.class)){
                    continue;
                }
                GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                String url = ("/" + baseUrl + "/" + requestMapping.value().trim()).replaceAll("/+", "/");
                handlerMapping.put(url, method);
            }
        }
    }

    //小写第一个字母
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatcher(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        uri = uri.replace(contextPath, "").replaceAll("/+", "/");
        if(!this.handlerMapping.containsKey(uri)){
            resp.getWriter().write("404 Not Found!");
        }
        Method method = this.handlerMapping.get(uri);
        //处理参数
        Map<String, String[]> params = req.getParameterMap();
        //获取所有的形参列表
        Parameter[] parameters = method.getParameters();
        //存放参数顺序的容器
        Object[] paramValue = new Object[parameters.length];
        for(int i = 0; i < parameters.length; i ++){
            Class<?> parameterType = parameters[i].getType();
            if(parameterType == HttpServletRequest.class){
                paramValue[i] = req;
                continue;
            }
            if(parameterType == HttpServletResponse.class){
                paramValue[i] = resp;
                continue;
            }
            if(parameterType == String.class){
                GPRequestParam gpRequestParam = parameters[i].getAnnotation(GPRequestParam.class);
                for(Map.Entry<String, String[]> entry : params.entrySet()){
                    if(entry.getKey().equals(gpRequestParam.value())){
                        paramValue[i] = entry.getValue();
                        continue;
                    }
                }
                continue;
            }
        }
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        DemoAction obj = (DemoAction)ioc.get(beanName);
        method.invoke(obj, (HttpServletRequest)paramValue[0], (HttpServletResponse)paramValue[1], ((String[])paramValue[2])[0] + "");
    }


}
